/*
 * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2009.
 *
 * Licensed under the Aduna BSD-style license.
 */

package edu.tju.commons.collections.iterators;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * An iterator that delays the creation of the underlying iterator until it is
 * being accessed. This is mainly useful for situations where iterator creation
 * adds considerable overhead but where the iterator may not actually be used,
 * or where a created iterator consumes scarce resources like JDBC-connections
 * or memory. Subclasses must implement the <tt>createIterator</tt> method,
 * which is called once when the iterator is first needed.
 */
public abstract class DelayedIterator<E> extends CloseableIteratorBase<E> {

	private Iterator<? extends E> iter;

	/**
	 * Creates the iterator that should be iterated over. This method is called
	 * only once, when the iterator is first needed.
	 */
	protected abstract Iterator<? extends E> createIterator();

	/**
	 * Calls the <tt>hasNext</tt> method of the underlying iterator.
	 */
	public boolean hasNext() {
		if (iter == null) {
			// Underlying iterator has not yet been initialized
			synchronized (this) {
				if (isClosed()) {
					return false;
				}
				else {
					iter = createIterator();
				}
			}
		}

		return iter.hasNext();
	}

	/**
	 * Calls the <tt>next</tt> method of the underlying iterator.
	 */
	public E next() {
		if (iter == null) {
			// Underlying iterator has not yet been initialized
			synchronized (this) {
				if (isClosed()) {
					throw new NoSuchElementException("Iterator has been closed");
				}
				else {
					iter = createIterator();
				}
			}
		}

		return iter.next();
	}

	/**
	 * Calls the <tt>remove</tt> method of the underlying iterator.
	 */
	public void remove() {
		if (iter == null || isClosed()) {
			throw new IllegalStateException();
		}

		iter.remove();
	}

	/**
	 * Closes this iterator as well as the underlying iterator if it has already
	 * been created and happens to be a {@link CloseableIterator}.
	 */
	@Override
	protected void handleClose() {
		super.handleClose();

		synchronized (this) {
			Iterators.closeCloseable(iter);
		}
	}
}
